// 被动处理dbg/simulator信息
// 1. 共用ZqCommPkg头信息，但是忽略里面的checksum信息
// 2. cmd01 - print debug string.
// 3. cmd02 - snapshot RX: ZqCommPkg; TX: ZqCommPkg[4] + i[1] + (pm_idx[1] + pm_value[2])[i*3] + j[1] + (st_idx[1] + st_value[4])[j*5] + k[1] + GPIO[5] + GPIO_MODE[5]
// 4. cmd03 - simuhw RX: ZqCommPkg + key[1] + i[1] + (pm_idx[1] + pm_value[2])[i*3] + j[1] + (st_idx[1] + st_value[4])[j*5] + k[1] + GPIO[5]; 
#include <stdarg.h>
#include "CH58x_common.h"
#include "zqm_global.h"
#include "zqm_module.h"
#include "hal_mcu.h"
#include "hal_uart.h"
#include "task_ble.h"
#include "task_ctrl.h"
#include "task_dbg.h"

// static DBG_MON_MODE dbg_mon_mode = DBG_MON_MODE_NONE;  // 监控模式, 配合PC模拟器使用
DBG_MON_MODE dbg_mon_mode = DBG_MON_MODE_NONE;  // 监控模式, 配合PC模拟器使用
#define DBG_UART_PKG_SIZE       (512+16)	    // 最大情况出现在OTA, 一帧大小为5+6+512
#define DBG_UART_RX_BUF_SIZE    (DBG_UART_PKG_SIZE * 2)
static uint8_t dbg_uart_rx_buf[DBG_UART_RX_BUF_SIZE];

// uart send/receive related low layer function  -----------------------------------------------------------------------------------------------
static uint8_t dbg_report_flag = 0;
uint8_t dbg_key = 0;
uint8_t dbg_recv_buf[UART1_RX_BUF_SIZE];

uint8_t dbg_gpio[5] = { 0 };        // 同步R32_PA_PIN, R32_PA_OUT, R32_PA_PIN, R32_PB_OUT
uint8_t dbg_gpio_mode[5] = { 0 };   // 同步 R32_PA_DIR, R32_PB_DIR
short *dbg_pms;
uint8_t dbg_st_buf[sizeof(ZqMcuStates)] = { 0 };

// buf和sz都要包含ZqCommPkg头
void DbgTx(uint8_t cmd, uint8_t* buf, uint16_t sz)
{
    // 生产模式下让出串口1
    if((dbg_mon_mode == DBG_MON_MODE_NONE || dbg_mon_mode == DBG_MON_MODE_UART) && (DBG_UART_PORT == 1) && (m0_diag.run_in_ftm))
        return;
    // 没有模拟器监控, 直接发送debug数据
    if(dbg_mon_mode == DBG_MON_MODE_NONE) { 
        UartSend(DBG_UART_PORT, buf + sizeof(ZqCommPkg), sz - sizeof(ZqCommPkg));
        return;
    }
    // UartSend(DBG_UART_PORT, buf + sizeof(ZqCommPkg), sz); // TODO 测试代码

    // 模拟器监控模式, 打包转发给模拟器
    ZqCommPkg* pkg = (ZqCommPkg*)buf;
    pkg->flag = COMM_PKG_HEAD_FLAG;
    pkg->len = sz;
    pkg->cmd = cmd;
    pkg->checksum = 0;
    pkg->checksum = ZqGetChecksum(buf, pkg->len);
    if(dbg_mon_mode == DBG_MON_MODE_UART) 
        UartSend(DBG_UART_PORT, buf, pkg->len);
    else if(dbg_mon_mode == DBG_MON_MODE_BLE)
        BleExchSend(buf, pkg->len);
    return;
}

// cmd01 - print debug string.
void DbgPrintf(char* fmt, ...)
{
    //不定参数格式化
    va_list argptr;
    va_start(argptr, fmt);
    char buf[512];
    int cnt = vsprintf(buf+sizeof(ZqCommPkg), fmt, argptr);
    va_end(argptr);
    DbgTx(COMM_CMD_DBG_LOG, (uint8_t *)buf, cnt+sizeof(ZqCommPkg));
    return;
}

// exchange dbg data with pc
// 接收: 包头ZqCommPkg[4] + 参数数量i[1] + 参数表(pm_idx[1] + pm_value[2])[i * 3] + 状态数量j[1] + 状态表(st_idx[1] + st_value[4])[j * 5] 
//	+ GPIO变化(bit0-3表示以下有效, gpioa mode, gpiob mode, gpioa, gpiob)k[1] + GPIO_MODE[5] + GPIO[5]
// 发送: 包头ZqCommPkg[4] + 查询命令(0不需查询，1全量查询，2增量查询)q[1] + 参数数量i[1] + 参数表(pm_idx[1] + pm_value[2])[i * 3] + 状态数量j[1] + 状态表(st_idx[1] + st_value[4])[j * 5] 
//	+ GPIO变化(bit0-5, 表示以下有效, gpioa mode, gpiob mode, gpioa, gpiob, adc, key)k[1] + GPIO_MODE[5] + GPIO[5] + ADC[4] + 键key[1]
static void DbgSnapshotResponse(void)
{
    uint8_t flag = 0;
    if (dbg_report_flag == 0) return; // 没有Snapshot需求
    else if (dbg_report_flag == 2)
        flag = 1; // =0 回复变更的项目, 否则要求所有信息快照以重新同步, 
    dbg_report_flag = 0;

    uint8_t buf[512];
    uint8_t* p = buf + sizeof(ZqCommPkg);
    uint8_t* num;
    uint8_t idx;
    // pack pm_val
    num = p++;
    for (idx = 0, *num = 0; idx < PM_NUM; idx++) {
        if (pms_spec[idx].val != dbg_pms[idx] || flag) {
            dbg_pms[idx] = pms_spec[idx].val;
            (*num)++;
            *p++ = idx;
            *p++ = (uint8_t)(dbg_pms[idx] & 0xff);
            *p++ = (uint8_t)((dbg_pms[idx] >> 8) & 0xff);
        }
    }
    *p++ = ZqGetChecksum((uint8_t *)dbg_pms, sizeof(dbg_pms));     // 附加checksum
    // pack zy_st
    num = p++;
    uint8_t* pst = (uint8_t*)&zy_st;
    for (idx = 0, *num = 0; idx < sizeof(ZqMcuStates); idx += 4) {
        if ((*(uint32_t*)(dbg_st_buf + idx) != *(uint32_t*)(pst + idx)) || flag) {
            (*num)++;
            *p++ = idx;
            *p++ = dbg_st_buf[idx] = pst[idx];
            *p++ = dbg_st_buf[idx+1] = pst[idx+1];
            *p++ = dbg_st_buf[idx+2] = pst[idx+2];
            *p++ = dbg_st_buf[idx+3] = pst[idx+3];
        }
    }
    *p++ = ZqGetChecksum(dbg_st_buf, sizeof(dbg_st_buf));     // 附加checksum

    // 同步IO状态，输入还是输出
    num = p++;
    *num = 0;
    if (dbg_gpio_mode[0] != R8_PA_DIR_0 || dbg_gpio_mode[1] != R8_PA_DIR_1) { // 侦测到GPIOA Mode变化，记录并报告
        *num |= 0x01;
        *p++ = dbg_gpio_mode[0] = R8_PA_DIR_0;
        *p++ = dbg_gpio_mode[1] = R8_PA_DIR_1;
    }
    if (dbg_gpio_mode[2] != R8_PB_DIR_0 || dbg_gpio_mode[3] != R8_PB_DIR_1 || dbg_gpio_mode[4] != R8_PB_DIR_2) { // 侦测到GPIOB Mode变化，记录并报告
        *num |= 0x02;
        *p++ = dbg_gpio_mode[2] = R8_PB_DIR_0;
        *p++ = dbg_gpio_mode[3] = R8_PB_DIR_1;
        *p++ = dbg_gpio_mode[4] = R8_PB_DIR_2;
    }
    for (int j = 0; j < 2; j++) {
        for (uint32_t i = 1; i <= 0x80; i <<= 1) {
            uint8_t x = (*(&R8_PA_DIR_0 + j) & i) ? (*(&R8_PA_OUT_0 + j) & i) : (*(&R8_PA_PIN_0 + j) & i); // 根据R8_PA_DIR判断输入还是输出, 取值R8_PA_OUT，还是R8_PA_PIN
            if (x != (dbg_gpio[j] & i)) { // 判断是否变化
                *num |= 0x04;
                dbg_gpio[j] ^= i; // 变化就取反
            }
        }
    }
    if (*num & 0x04) {
        *p++ = dbg_gpio[0];
        *p++ = dbg_gpio[1];
    }
    for (int j = 0; j < 3; j++) {
        for (uint32_t i = 1; i <= 0x80; i <<= 1) {
            uint8_t x = (*(&R8_PB_DIR_0 + j) & i) ? (*(&R8_PB_OUT_0 + j) & i) : (*(&R8_PB_PIN_0 + j) & i); // 根据R8_PB_DIR判断输入还是输出, 取值R8_PB_OUT，还是R8_PB_PIN
            if (x != (dbg_gpio[j+2] & i)) {
                *num |= 0x08;
                dbg_gpio[j + 2] ^= i;
            }
        }
    }
    if (*num & 0x08) {
        *p++ = dbg_gpio[2];
        *p++ = dbg_gpio[3];
        *p++ = dbg_gpio[4];
    }
    // pack key and display buffer
    extern uint32_t key_state[ZQ_KEY_NUM];
    memcpy(p, key_state, sizeof(key_state));
    p += sizeof(key_state);
    extern uint8_t disp_memory[DISP_MEMORY_SIZE];
    memcpy(p, disp_memory, sizeof(disp_memory));
    p += sizeof(disp_memory);
    DbgTx(COMM_CMD_DBG_SNAP, buf, (p - buf));
    return;
}
// 模拟硬件输入, 传入key值; Tx: ZqCommPkg + key(1); Rx: 无
extern void PmSave(void);
void DbgExchangeData(ZqCommPkg* pkg)
{
    uint8_t* buf = pkg->data;
    uint8_t num = 0;
    //int idx = 0;
    // 解包查询device状态，完全回复还是增量回复
    dbg_report_flag = *buf++;
    // 解包参数修改
    num = *buf++;
    for (uint8_t i = 0; i < num; i++) {
        uint8_t idx = *buf++;
        PmSetValue(idx, (short)(*buf | (*(buf+1) << 8)));
        buf += 2;
    }
    if (num > 0) PmSave();
    // 解包状态修改
    num = *buf++;
    for (uint8_t i = 0; i < num; i++) {
        uint8_t idx = *buf++;
        uint8_t* pst = ((uint8_t *)&zy_st) + idx;
        pst[0] = *buf++;
        pst[1] = *buf++;
        pst[2] = *buf++;
        pst[3] = *buf++;
    }
    //memcpy(simu_hw.adc, &pkg->data[5], sizeof(simu_hw.adc));
    // 解包key修改
    num = *buf++;
    if ((num & 0x20) == 0x20) {
        dbg_key = *buf++;
    }

#ifdef ZQ_SIMULATOR // 模拟器，接受其他参数
    uint32_t gpioa = 0, gpiob = 0;
    // 解包GPIOA修改
    if ((num & 0x04) == 0x04) {
        gpioa = *buf++;
        gpioa |= ((*buf++) << 8);
    }
    // 解包GPIOB修改
    if ((num & 0x08) == 0x08) {
        gpiob = *buf++;
        gpiob |= ((*buf++) << 8);
        gpiob |= ((*buf++) << 16);
    }
    // TODO zy_ios.batt_chrg 模拟电池脉冲下降沿触发
    if (ZyIsGPIOValid(zy_ios.batt_chrg)) {
#if (ZQ_DISPLAY_PANEL_TYPE == DPT_INTEGRAL_3y)
        if ((simu_hw.gpiob & zy_ios.batt_chrg) != 0 && (gpiob & zy_ios.batt_chrg) == 0)
            GPIOB_IRQHandler();
#else
        if ((simu_hw.gpioa & zy_ios.batt_chrg) != 0 && (gpioa & zy_ios.batt_chrg) == 0)
            GPIOA_IRQHandler();
#endif
    }
    // TODO  特例，休眠状态下配置是的CAT1_EN为低
    //if ((byte*)&simu_hw.gpiob_mode[5] == GPIO_ModeIN_PD) 
    //    hw->gpio_b[5] = (byte*)&simu_hw.gpio_b[5];
    for (int i = 0; i < 16; i++) {
        uint32_t mask = 1 << i;
        if ((simu_hw.gpioa_d & mask) == 0)
            simu_hw.gpioa = (gpioa & mask) ? (simu_hw.gpioa | mask) : (simu_hw.gpioa & (~mask));
    }
    for (int i = 0; i < 24; i++) {
        uint32_t mask = 1 << i;
        if ((simu_hw.gpiob_d & mask) == 0)
            simu_hw.gpiob = (gpiob & mask) ? (simu_hw.gpiob | mask) : (simu_hw.gpiob & (~mask));
    }
    // 解包ADC修改
    if ((num & 0x10) == 0x10) {
        memcpy(simu_hw.adc, buf, sizeof(simu_hw.adc));
        buf += sizeof(simu_hw.adc);
    }
#endif // ZQ_SIMULATOR
}

void DbgQueryDeviceCfg(ZqCommPkg* pkg)
{
    uint8_t buf[512];
    uint8_t* p = buf + sizeof(ZqCommPkg);

    // pack device type, 32bits 
    *(p++) = (uint8_t)CFG_DEVICE_TYPE;
    *(p++) = (uint8_t)(CFG_DEVICE_TYPE >> 8);
    *(p++) = (uint8_t)(CFG_DEVICE_TYPE >> 16);
    *(p++) = (uint8_t)(CFG_DEVICE_TYPE >> 24);
    // pack display type
    *(p++) = ZQ_DISPLAY_PANEL_TYPE;
    // pack gpio definition table
    *(p++) = sizeof(zy_ios);
    memcpy(p, &zy_ios, sizeof(zy_ios));
    p += sizeof(zy_ios);
    *(p++) = PM_NUM;
    // pack parameters definition
    uint8_t * ptab = (uint8_t *)pms_spec;
    for (int i = 0; i < PM_NUM; i++) { // pack param table
        for(int j = 0; j < sizeof(pms_spec[i]); j++)
            *(p++) = *ptab++;
    }    
    DbgTx(COMM_CMD_DBG_CFG, buf, (p - buf));
    return;
}

static void DbgRx_cb(ZqCommPkg* pkg)
{
        if(dbg_mon_mode != DBG_MON_MODE_BLE)
            dbg_mon_mode = DBG_MON_MODE_UART;
        DelayMs(5);
        switch (pkg->cmd)
        {
            //case 0x01:    // print debug info, only send, not receive.
        case COMM_CMD_DBG_SNAP:      // Snapshot, 处理时间较长，只设置标志
            //dbg_report_flag = pkg->data[0] | 0x80;
            DbgExchangeData(pkg);
            //DbgSnapshot(pkg);
            break;
        case COMM_CMD_DBG_CFG:      // Query params table
            DbgQueryDeviceCfg(pkg);
            //DbgSnapshot(pkg);
            break;
        default:
            break;
        }
    return;
}

void DbgTaskInit(void)
{
    dbg_pms = malloc(PM_NUM * sizeof(short));
    dbg_mon_mode = DBG_MON_MODE_NONE;
    UartInit(DBG_UART_PORT, 115200, dbg_uart_rx_buf, DBG_UART_RX_BUF_SIZE, NULL); 
}

void DbgTask(void)
{
    if(BleGetGapRoleState() == GAPROLE_CONNECTED) {
        dbg_mon_mode = DBG_MON_MODE_BLE;
    }
    else if(dbg_mon_mode == DBG_MON_MODE_BLE) {
        dbg_mon_mode = DBG_MON_MODE_NONE;
    }

    if(dbg_mon_mode == DBG_MON_MODE_NONE || dbg_mon_mode == DBG_MON_MODE_UART) {
        UartRecv(DBG_UART_PORT, DbgRx_cb);
    }
    else if(dbg_mon_mode == DBG_MON_MODE_BLE) {
        BleExchRecv(DbgRx_cb);
    }

    DbgSnapshotResponse();
}